﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Babelfish.MetaData;
using Newtonsoft.Json.Linq;
using Babelfish.Node;

namespace Babelfish.JSON
{
    /// <summary>
    /// 
    /// </summary>
    public class JSONNode : GeneralNode, INode
    {
        /// <summary>
        /// Gets or sets the token.
        /// </summary>
        /// <value>The token.</value>
        protected JToken Token { get; set; }

        /// <summary>
        /// Removes this this node from its parent node.
        /// </summary>
        public void Remove(INode childNode)
        {

        }

        private AttributeCollection _attributes;
        /// <summary>
        /// Gets the attributes of this node.
        /// </summary>
        /// <value>The attributes.</value>
        public AttributeCollection Attributes
        {
            get
            {
                _attributes = _attributes ?? GetAttributes();
                return _attributes;
            }
        }

        /// <summary>
        /// Gets the attributes.
        /// </summary>
        /// <returns></returns>
        private AttributeCollection GetAttributes()
        {
            return new AttributeCollection {new GeneralAttribute {Name = "type", Value = Token.Type.ToString()}};
        }

        private string _name;
        /// <summary>
        /// Gets the name of this node.
        /// </summary>
        /// <value>The name.</value>
        public string Name
        {
            get
            {
                _name = _name ?? (Token is JProperty ? (Token as JProperty).Name : string.Empty);
                return _name;
            }
            set
            {
                _name = value;
            }
        }

        private string _text;
        /// <summary>
        /// Gets the value of this node.
        /// </summary>
        /// <value>The value.</value>
        public string Text
        {
            get
            {
                _text = _text ?? GetTokenText(Token);
                return _text;
            }
        }


        /// <summary>
        /// Gets the token text.
        /// </summary>
        /// <param name="node">The node.</param>
        /// <returns></returns>
        private static string GetTokenText(JToken node)
        {
            switch (node.Type)
            {
                case JTokenType.Boolean:
                    return node.Value<bool>().ToString();
                case JTokenType.Bytes:
                    return node.Value<byte[]>().ToString();
                case JTokenType.Date:
                    return node.Value<DateTime>().ToString();
                case JTokenType.Float:
                    return node.Value<float>().ToString();
                case JTokenType.Integer:
                    return node.Value<int>().ToString();
                case JTokenType.String:
                    return node.Value<string>().ToString();
                case JTokenType.Property:
                    return GetChildValue(node.Children().First());
                default:
                    return string.Empty;
            }
        }

        /// <summary>
        /// Gets the child value.
        /// </summary>
        /// <param name="node">The node.</param>
        /// <returns></returns>
        private static string GetChildValue(JToken node)
        {
            switch (node.Type)
            {
                case JTokenType.Boolean:
                    return node.Value<bool>().ToString();
                case JTokenType.Bytes:
                    return node.Value<byte[]>().ToString();
                case JTokenType.Date:
                    return node.Value<DateTime>().ToString();
                case JTokenType.Float:
                    return node.Value<float>().ToString();
                case JTokenType.Integer:
                    return node.Value<int>().ToString();
                case JTokenType.String:
                    return node.Value<string>().ToString();
                default:
                    return string.Empty;
            }
        }

        private MetaDataCollection _metaData;
        /// <summary>
        /// Gets the metadata of this node, like html or xml comments
        /// </summary>
        /// <value></value>
        public MetaDataCollection MetaData
        {
            get
            {
                _metaData = _metaData ?? GetMetaData();
                return _metaData;
            }
        }

        /// <summary>
        /// Gets the meta data.
        /// </summary>
        /// <returns></returns>
        private MetaDataCollection GetMetaData()
        {
            var collection = new MetaDataCollection();

            foreach (var child in Token.Values())
                if (child.Type == JTokenType.Comment)
                    collection.Add(new GeneralMetaData { Name = string.Empty, Value = child.ToString() });

            return collection;
        }


        private bool? _hasValue;
        /// <summary>
        /// Gets a value indicating whether this node has a value.
        /// </summary>
        /// <value><c>true</c> if this node has a value; otherwise, <c>false</c>.</value>
        public bool HasValue
        {
            get
            {
                _hasValue = _hasValue ?? (Token.HasValues && Token.Values().Count() == 1) || !Token.HasValues;
                return _hasValue.Value;
            }
        }

        /// <summary>
        /// Gets the parent node of this node.
        /// </summary>
        /// <value>The parent node.</value>
        public INode Parent { get; set; }


        private NodeCollection _siblings;
        /// <summary>
        /// Gets the siblings of this node.
        /// </summary>
        /// <value>The sibling nodes.</value>
        public NodeCollection Siblings
        {
            get
            {
                _siblings = _siblings ?? new NodeCollection(Parent.ChildNodes.Where(node => node != this));
                return _siblings;
            }
        }

        private NodeCollection _childNodes;
        /// <summary>
        /// Gets the child nodes of this node.
        /// </summary>
        /// <value>The child nodes.</value>
        public NodeCollection ChildNodes
        {
            get
            {
                _childNodes = _childNodes ?? GetChildNodes();
                return _childNodes;
            }
        }

        /// <summary>
        /// Gets the child nodes.
        /// </summary>
        /// <returns></returns>
        private NodeCollection GetChildNodes()
        {
            var collection = new NodeCollection();

            if (!Token.HasValues)
                return collection;

            foreach (var child in Token.Children())
            {
                if (child is JValue)
                    continue;

                if (child.Type == JTokenType.Array || child.Type == JTokenType.Object)
                    foreach (var arrayChild in child.Children())
                        collection.Add(new JSONNode { Token = arrayChild, Parent = this });
                else
                    collection.Add(new JSONNode { Token = child, Parent = this });

            }
            _childNodes = collection;
            return collection;
        }
    }
}
